﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Yahoo.Yui.Compressor;
using System.IO;
using System.Text;
using PersonalWebSite.Controllers;
using System.Configuration;

namespace PersonalWebSite.Common
{
    /// <summary>
    /// Defines HtmlHelper extension methods
    /// </summary>
    public static class HtmlHelperExtensions
    {
        #region Variables
        public const string cacheKeyBaseClientRefs = "ClientReferences";
        #endregion

        /// <summary>
        /// Gets the ClientReferencesManager
        /// </summary>
        /// <param name="viewContext">viewContext</param>
        /// <returns>ClientReferencesManager</returns>
        public static ClientReferencesManager ClientReferencesManager(this ViewContext viewContext)
        {
            return ((BaseController)viewContext.Controller).ClientReferencesManager;
        }

        /// <summary>
        /// Gets the NavigationManager instance
        /// </summary>
        /// <param name="viewContext">viewContext</param>
        /// <returns>NavigationManager</returns>
        public static NavigationManager NavigationManager(this HtmlHelper htmlHelper)
        {
            return new NavigationManager(htmlHelper.ViewContext.HttpContext.Session);
        }

        /// <summary>
        /// Renders the registered JS and CSS references
        /// </summary>
        /// <param name="htmlHelper">HtmlHelper</param>
        /// <returns>HTML code with CSS and JS refs</returns>
        public static IHtmlString CssAndJavascriptReferences(this HtmlHelper htmlHelper)
        {
            var clientReferencesManager = htmlHelper.ViewContext.ClientReferencesManager();

            UrlHelper urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection);

            if (HttpContext.Current.IsDebuggingEnabled)
            {
                string cssIncludesString = string.Join("\r\n", clientReferencesManager.CssFiles.Select(f => "<link href=\"" + urlHelper.Content(f) + "\" rel=\"stylesheet\" type=\"text/css\" />"));
                string jsIncludesString = string.Join("\r\n", clientReferencesManager.JsFiles.Select(f => "<script src=\"" + urlHelper.Content(f) + "\" type=\"text/javascript\"></script>"));
                return htmlHelper.Raw(cssIncludesString + "\r\n\r\n" + jsIncludesString);
            }

            bool isRecached = false;

            string cacheKey = cacheKeyBaseClientRefs + HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path);

            var cssFilesInfo = clientReferencesManager.CssFiles.Select(p => new ClientReferencesManager.FileInfo { Path = p, PhysicalPath = HttpContext.Current.Server.MapPath(p) });
            var jsFilesInfo = clientReferencesManager.JsFiles.Select(p => new ClientReferencesManager.FileInfo { Path = p, PhysicalPath = HttpContext.Current.Server.MapPath(p) });
            var cacheDependency = new System.Web.Caching.CacheDependency(cssFilesInfo.Select(f => f.PhysicalPath).Concat(jsFilesInfo.Select(f => f.PhysicalPath)).ToArray());

            var clientRefs = Cache.CacheItem(cacheKey, () => new ClientReferencesManager.UrlClientReferences
            {
                CssFiles = cssFilesInfo,
                JsFiles = jsFilesInfo
            }, cacheDependency, (key, item, reason) => { });

            if (clientRefs.JsFiles.Count() != clientReferencesManager.JsFiles.Length || clientRefs.CssFiles.Count() != clientReferencesManager.CssFiles.Length)
            {
                isRecached = true;
                Cache.RemoveItem(cacheKey);
                clientRefs = Cache.CacheItem(cacheKey, () => new ClientReferencesManager.UrlClientReferences
                {
                    CssFiles = cssFilesInfo,
                    JsFiles = jsFilesInfo
                }, cacheDependency, (key, item, reason) => { });
            }

            //Read file contents minified
            StringBuilder stringBuilderFullContentCss = new StringBuilder();
            StringBuilder stringBuilderFullContentJs = new StringBuilder();
            clientRefs.CssFiles.Where(f => f.Content == null).ToList().ForEach(f =>
            {
                using (var streamReader = new StreamReader(f.PhysicalPath))
                    f.Content = CssCompressor.Compress(streamReader.ReadToEnd());
                stringBuilderFullContentCss.Append(f.Content);
            });
            clientRefs.JsFiles.Where(f => f.Content == null).ToList().ForEach(f =>
            {
                using (var streamReader = new StreamReader(f.PhysicalPath))
                    f.Content = JavaScriptCompressor.Compress(streamReader.ReadToEnd());
                stringBuilderFullContentJs.Append(f.Content);
            });
            if (stringBuilderFullContentCss.Length > 0)
            {
                isRecached = true;
                clientRefs.CssMinifiedAndCombinedContent = stringBuilderFullContentCss.ToString().Replace("../img/", "../Content/img/");
            }
            if (stringBuilderFullContentJs.Length > 0)
            {
                isRecached = true;
                clientRefs.JsMinifiedAndCombinedContent = stringBuilderFullContentJs.ToString();
            }

            TagBuilder tagBuilderCss = new TagBuilder("link");
            tagBuilderCss.Attributes.Add("href", urlHelper.Action("Css", "ClientReferences", new { cacheKey = cacheKey, version = ConfigurationManager.AppSettings["Version"] }));
            tagBuilderCss.Attributes.Add("rel", "stylesheet");
            tagBuilderCss.Attributes.Add("type", "text/css");

            TagBuilder tagBuilderJs = new TagBuilder("script");
            tagBuilderJs.Attributes.Add("src", urlHelper.Action("Js", "ClientReferences", new { cacheKey = cacheKey, version = ConfigurationManager.AppSettings["Version"] }));
            tagBuilderJs.Attributes.Add("type", "text/javascript");

            return htmlHelper.Raw(tagBuilderCss.ToString(TagRenderMode.SelfClosing) + tagBuilderJs.ToString(TagRenderMode.Normal));
        }
    }
}